package hsb.ruoyi.common.core.domain;


import com.usthe.sureness.subject.Subject;
import hsb.ruoyi.common.constant.CacheConstants;
import hsb.ruoyi.common.constant.Constants;
import hsb.ruoyi.common.core.cache.LocalCache;
import hsb.ruoyi.common.core.domain.model.JwtSubject;
import hsb.ruoyi.common.core.domain.model.LoginUser;
import hsb.ruoyi.common.utils.StringUtils;
import io.smallrye.jwt.build.Jwt;
import io.smallrye.jwt.util.KeyUtils;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.impl.jose.JWT;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import javax.crypto.SecretKey;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.core.Context;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static hsb.ruoyi.common.constant.CacheConstants.LOGIN_SINGLE_TOKEN_KEY;


/**
 * token验证处理
 *
 * @author ruoyi
 */
@Singleton
public class TokenService {
    // 令牌自定义标识
    @ConfigProperty(name = "token.header")
    private String header;

    // 令牌秘钥
    @ConfigProperty(name = "token.secret")
    private String secret;

    // 令牌有效期（默认30分钟）
    @ConfigProperty(name = "token.expireTime")
    private int expireTime;

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;

    @Inject
    LocalCache localCache;


    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public Subject geSubject(HttpServerRequest request) {
        // 获取请求携带的令牌
        String token = getToken(request);
        if (StringUtils.isNotEmpty(token)) {
            try {
                JsonObject claims = parseToken(token);
                // 解析对应的权限以及用户信息
                String uuid = claims.getString(Constants.LOGIN_USER_KEY);
                String userKey = getTokenKey(uuid);
                return localCache.getCacheObject(userKey);
            } catch (Exception e) {
            }
        }
        return null;
    }

    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public Subject geSubject(String token) {
        if (StringUtils.isNotEmpty(token)) {
            if (token.startsWith(Constants.TOKEN_PREFIX)) {
                token = token.replace(Constants.TOKEN_PREFIX, "");
            }
            try {
                JsonObject claims = parseToken(token);
                // 解析对应的权限以及用户信息
                String uuid = claims.getString(Constants.LOGIN_USER_KEY);
                String userKey = getTokenKey(uuid);
                return localCache.getCacheObject(userKey);
            } catch (Exception e) {
            }
        }
        return null;
    }

    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUser getLoginUser(HttpServerRequest request) {
        // 获取请求携带的令牌
        Subject subject = geSubject(request);
        if (subject != null) {
            return (LoginUser) subject.getCredential();
        }
        return null;


    }

    /**
     * 设置用户身份信息
     */
    public void setLoginUser(LoginUser loginUser) {
        if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) {
            refreshToken(loginUser);
        }
    }

    /**
     * 删除用户身份信息
     */
    public void delLoginUser(String token) {
        if (StringUtils.isNotEmpty(token)) {
            String userKey = getTokenKey(token);
            localCache.deleteObject(userKey);
        }
    }

    /**
     * 创建令牌
     *
     * @param loginUser 用户信息
     * @return 令牌
     */
    public String createToken(LoginUser loginUser) {
        // String token = IdUtils.fastUUID();
        String token = UUID.randomUUID().toString();
        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.LOGIN_USER_KEY, token);
        String jwt = createToken(claims);


        JwtSubject jwtSubject = JwtSubject.builder(jwt).setOwnRoles(loginUser.getRoles()).build();
        jwtSubject.setPrincipal(loginUser);

        loginUser.setToken(token);
        setUserAgent(loginUser);
        refreshToken(jwtSubject);

        //根据用户id，防止重复登陆
        disableRepeatedLogin(loginUser);

        return jwt;
    }

    private void disableRepeatedLogin(LoginUser loginUser) {
        Long userId = loginUser.getUserId();
        String typePrefix = loginUser.getTypePrefix();
        String loginKey = typePrefix + userId;
        loginKey = LOGIN_SINGLE_TOKEN_KEY + loginKey;
        String userKey = getTokenKey(loginUser.getToken());
        localCache.setCacheObject(loginKey, userKey, expireTime, TimeUnit.MINUTES);
    }


    /**
     * 验证令牌有效期，相差不足20分钟，自动刷新缓存
     *
     * @param loginUser
     * @return 令牌
     */
    public void verifyToken(LoginUser loginUser) {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
            refreshToken(loginUser);
        }
    }

    public void refreshToken(JwtSubject jwtSubject) {
        LoginUser loginUser = (LoginUser) jwtSubject.getPrincipal();
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
        // 根据uuid将loginUser缓存
        String userKey = getTokenKey(loginUser.getToken());
        localCache.setCacheObject(userKey, jwtSubject, expireTime, TimeUnit.MINUTES);
        //    Object cacheObject = localCache.getCacheObject(userKey);
        //    System.out.println(cacheObject);
//        LoginUser loginUser = (LoginUser)jwtSubject.getPrincipal();
//        refreshToken(loginUser);
    }

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(LoginUser loginUser) {
        String userKey = getTokenKey(loginUser.getToken());
        JwtSubject jwtSubject = localCache.getCacheObject(userKey);
        refreshToken(jwtSubject);
        //因为现在都是在内存中，并且没有使用序列化存数，改动之后是不需要调用这个方法刷新内存的，只是需要刷新时间
//        loginUser.setLoginTime(System.currentTimeMillis());
//        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
//        // 根据uuid将loginUser缓存
//        String userKey = getTokenKey(loginUser.getToken());
//        localCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
    }

    /**
     * 踢人下载
     *
     * @param type_id type (用户类型)  下划线    id(用户id)
     *                type 取值 app | sys
     *                <p>
     *                比如： app_1  ,  sys_1
     */
    public void kicOut(String type_id) {
        String loginKey = LOGIN_SINGLE_TOKEN_KEY + type_id;
        Object cacheToken = localCache.getCacheObject(loginKey);
        if (cacheToken != null) {
            localCache.deleteObject(cacheToken.toString());
            localCache.deleteObject(loginKey);
        }
    }


    /**
     * 设置用户代理信息
     *
     * @param loginUser 登录信息
     */
    public void setUserAgent(LoginUser loginUser) {
        // UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        // String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        // loginUser.setIpaddr(ip);
        // loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        // loginUser.setBrowser(userAgent.getBrowser().getName());
        //  loginUser.setOs(userAgent.getOperatingSystem().getName());
    }

    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private String createToken(Map<String, Object> claims) {
        return Jwt.claims(claims).signWithSecret(secret);
    }

    public static void main(String[] args) {
        String customClaim = Jwt.claims(Collections.singletonMap("customClaim", "custom-value")).signWithSecret("1232132refefefefgrgggggggggrgrg113");
        System.out.println(customClaim);
        SecretKey secretKeyFromSecret = KeyUtils.createSecretKeyFromSecret("1232132refefefefgrgggggggggrgrg113");
        System.out.println(secretKeyFromSecret);
        //   JwtClaims jwtClaims = new JwtClaims();
        //KeyUtils.loadJsonWebKeys()
        //new DefaultJWTTokenParser().parse(jwt, contextInfo);
        JsonObject parse = JWT.parse(customClaim);
        System.out.println(parse);

        String string = parse.getJsonObject("payload").getString("customClaim");
        System.out.println(string);
        // Jwt.claims().
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private JsonObject parseToken(String token) {
        JsonObject parse = JWT.parse(token);

        return parse.getJsonObject("payload");//.getString("login_user_key");


//
//        JsonObject parse = JWT.parse(token);
//     //   parse
//        return Jwts.parser()
//                .setSigningKey(secret)
//                .parseClaimsJws(token)
//                .getBody();
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        throw new RuntimeException("未实现");
//        JsonObject claims = parseToken(token);
//        return claims.getSubject();
    }

    /**
     * 获取请求token
     *
     * @param request
     * @return token
     */
    private String getToken(@Context HttpServerRequest request) {
        String token = request.getHeader(header);
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
    }

    private String getTokenKey(String uuid) {
        return CacheConstants.LOGIN_TOKEN_KEY + uuid;
    }
}
